import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, LessThan, MoreThan } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { User } from './entities/user.entity';
import { RefreshToken } from './entities/refresh-token.entity';
import { Friendship } from '../friends/entities/friendship.entity';
import { FriendshipStatus } from '../friends/entities/friendship.entity';
import * as bcrypt from 'bcrypt';
import * as crypto from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import type { AuthResponse, JwtPayload } from './interfaces';
import type { RegisterDto } from './dto';
import type { UserDto } from './dto/user.dto';
import type { FindUserDto } from './dto/find-user.dto';
import type { UpdateUserDto } from './dto/update-user.dto';
import type { TokenResponseDto } from './dto/refresh-token.dto';
import { DeviceFingerprintUtil } from '../../common/utils/device-fingerprint.util';
import { WechatUtil } from '../../common/utils/wechat.util';
import type { Request } from 'express';
import type { WechatLoginDto } from './dto/wechat-login.dto';
import { Gender } from './entities/user.entity';

/**
 * 认证服务
 * 处理用户认证相关的业务逻辑
 */
@Injectable()
export class AuthService {
  private readonly logger = new Logger(AuthService.name);

  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectRepository(RefreshToken)
    private readonly refreshTokenRepository: Repository<RefreshToken>,
    @InjectRepository(Friendship)
    private readonly friendshipRepository: Repository<Friendship>,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
  ) { }

  /**
   * 用户登录（支持双 Token 机制）
   * @param account 账号（用户名或手机号）
   * @param password 明文密码
   * @param request Express Request 对象（用于获取设备信息）
   * @returns 用户信息、Access Token 和 Refresh Token
   */
  async login(
    account: string,
    password: string,
    request?: Request,
  ): Promise<(AuthResponse & { refreshToken: string }) | null> {
    // 查找用户（支持用户名或手机号登录）
    const user = await this.userRepository.findOne({
      where: [{ username: account }, { phone: account }],
    });

    if (!user) {
      this.logger.warn(`登录失败：账号 ${account} 不存在`);
      return null;
    }

    // 验证密码
    if (!user.password) {
      this.logger.warn(
        `登录失败：账号 ${account} 没有设置密码（可能是微信登录用户）`,
      );
      return null;
    }
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      this.logger.warn(`登录失败：账号 ${account} 密码错误`);
      return null;
    }

    // 生成 Access Token
    const payload: JwtPayload = {
      sub: user.id,
      username: user.username,
      email: user.email,
    };

    const accessToken = await this.jwtService.signAsync(payload);

    // 生成 Refresh Token
    const refreshToken = await this.createRefreshToken(user.id, request);

    // 查询好友总数
    const friendsCount = await this.friendshipRepository.count({
      where: [
        { userId: user.id, status: FriendshipStatus.ACCEPTED },
        { friendId: user.id, status: FriendshipStatus.ACCEPTED },
      ],
    });

    // 返回用户信息和 token（排除密码字段）
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { password: _, ...safeUser } = user;

    this.logger.log(`用户 ${user.username || user.phone || account} 登录成功`);
    return {
      user: {
        ...safeUser,
        friendsCount,
      },
      accessToken,
      refreshToken: refreshToken.token,
    };
  }

  /**
   * 用户注册
   * @param registerDto 注册信息
   * @returns 新用户信息或 null（注册失败）
   */
  async register(
    registerDto: RegisterDto,
  ): Promise<Omit<User, 'password'> | null> {
    const { username, email, phone, password } = registerDto;

    // 检查用户名是否已存在
    const existingUserByUsername = await this.userRepository.findOne({
      where: { username },
    });
    if (existingUserByUsername) {
      this.logger.warn(`注册失败：用户名 ${username} 已存在`);
      return null;
    }

    // 检查邮箱是否已存在
    const existingUserByEmail = await this.userRepository.findOne({
      where: { email },
    });
    if (existingUserByEmail) {
      this.logger.warn(`注册失败：邮箱 ${email} 已被使用`);
      return null;
    }

    // 检查手机号是否已存在（如果提供了手机号）
    if (phone) {
      const existingUserByPhone = await this.userRepository.findOne({
        where: { phone },
      });
      if (existingUserByPhone) {
        this.logger.warn(`注册失败：手机号 ${phone} 已被使用`);
        return null;
      }
    }

    // 使用 bcrypt 对密码进行哈希（自动加盐）
    const saltRounds = 10;
    const hashedPassword = await bcrypt.hash(password, saltRounds);

    // 创建新用户
    const newUser = this.userRepository.create({
      username,
      email,
      phone, // 添加手机号（可选）
      password: hashedPassword, // bcrypt 哈希后的密码
      isActive: true,
    });

    const savedUser = await this.userRepository.save(newUser);
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { password: _, ...safeUser } = savedUser;

    this.logger.log(`用户 ${username} 注册成功`);
    return safeUser;
  }

  /**
   * 根据用户 ID 获取用户信息
   * @param id 用户 ID
   * @returns 返回用户信息（不含密码）或 null
   */
  async getUser(id: number): Promise<UserDto | null> {
    const user = await this.userRepository.findOne({
      where: { id },
      select: [
        'id',
        'username',
        'email',
        'phone',
        'isActive',
        'createdAt',
        'updatedAt',
      ],
    });
    return user as UserDto | null;
  }

  /**
   * 根据多种条件筛选用户列表
   * @param conditions 查询条件
   * @returns 返回用户列表和总数
   */
  async getUserList(conditions: FindUserDto): Promise<{
    users: UserDto[];
    total: number;
    page: number;
    pageSize: number;
  }> {
    const {
      id,
      username,
      email,
      phone,
      isActive,
      keyword,
      page = 1,
      pageSize = 10,
    } = conditions;

    // 创建查询构建器
    const queryBuilder = this.userRepository
      .createQueryBuilder('user')
      .select([
        'user.id',
        'user.username',
        'user.email',
        'user.phone',
        'user.isActive',
        'user.createdAt',
        'user.updatedAt',
      ]) // 不查询密码字段
      .skip((page - 1) * pageSize)
      .take(pageSize)
      .orderBy('user.createdAt', 'DESC'); // 按创建时间倒序

    // 精确匹配条件
    if (id) {
      queryBuilder.andWhere('user.id = :id', { id });
    }
    if (username) {
      queryBuilder.andWhere('user.username = :username', { username });
    }
    if (email) {
      queryBuilder.andWhere('user.email = :email', { email });
    }
    if (phone) {
      queryBuilder.andWhere('user.phone = :phone', { phone });
    }
    if (isActive !== undefined) {
      queryBuilder.andWhere('user.isActive = :isActive', { isActive });
    }

    // 关键词模糊搜索（搜索用户名或邮箱）
    if (keyword) {
      queryBuilder.andWhere(
        '(user.username LIKE :keyword OR user.email LIKE :keyword)',
        { keyword: `%${keyword}%` },
      );
    }

    // 执行查询
    const [users, total] = await queryBuilder.getManyAndCount();

    this.logger.log(`查询用户列表，共 ${total} 条记录`);

    return {
      users: users as UserDto[],
      total,
      page,
      pageSize,
    };
  }

  /**
   * 根据条件查询单个用户
   * @param conditions 查询条件（精确匹配）
   * @returns 返回用户信息（不含密码）或 null
   */
  async getUserByConditions(
    conditions: Partial<Pick<User, 'id' | 'username' | 'email' | 'phone'>>,
  ): Promise<UserDto | null> {
    console.log('conditions:', conditions);
    if (Object.keys(conditions).length === 0) {
      this.logger.warn('查询用户时未提供任何条件');
      return null;
    }

    // 过滤掉 null 和 undefined 值
    type UserConditions = Partial<
      Pick<User, 'id' | 'username' | 'email' | 'phone'>
    >;
    const filteredConditions: UserConditions = {};
    for (const [key, value] of Object.entries(conditions)) {
      if (value !== null && value !== undefined) {
        (filteredConditions as Record<string, unknown>)[key] = value;
      }
    }

    const user = await this.userRepository.findOne({
      where: filteredConditions,
      select: [
        'id',
        'username',
        'email',
        'phone',
        'isActive',
        'createdAt',
        'updatedAt',
      ],
    });

    return user as UserDto | null;
  }

  /**
   * 更新用户信息
   * @param updateUserDto 更新用户信息
   * @returns 更新后的用户信息或错误消息
   */
  async updateUser(
    updateUserDto: UpdateUserDto,
  ): Promise<Omit<User, 'password'> | string> {
    const { id, ...updateData } = updateUserDto;

    // 检查用户是否存在
    const user = await this.userRepository.findOne({ where: { id } });
    if (!user) {
      this.logger.warn(`更新失败：用户 ID ${id} 不存在`);
      return '用户不存在';
    }

    // 如果更新用户名，检查是否已被占用
    if (updateData.username && updateData.username !== user.username) {
      const existingUser = await this.userRepository.findOne({
        where: { username: updateData.username },
      });
      if (existingUser) {
        this.logger.warn(`更新失败：用户名 ${updateData.username} 已被占用`);
        return '用户名已被占用';
      }
    }

    // 如果更新邮箱，检查是否已被占用
    if (updateData.email && updateData.email !== user.email) {
      const existingUser = await this.userRepository.findOne({
        where: { email: updateData.email },
      });
      if (existingUser) {
        this.logger.warn(`更新失败：邮箱 ${updateData.email} 已被占用`);
        return '邮箱已被占用';
      }
    }

    // 如果更新手机号，检查是否已被占用
    if (updateData.phone && updateData.phone !== user.phone) {
      const existingUser = await this.userRepository.findOne({
        where: { phone: updateData.phone },
      });
      if (existingUser) {
        this.logger.warn(`更新失败：手机号 ${updateData.phone} 已被占用`);
        return '手机号已被占用';
      }
    }

    // 更新用户信息
    Object.assign(user, updateData);
    const updatedUser = await this.userRepository.save(user);

    // 排除密码字段
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { password: _, ...safeUser } = updatedUser;

    this.logger.log(`用户 ${user.username} 信息更新成功`);
    return safeUser;
  }

  /**
   * 删除用户
   * @param id 用户id
   * @returns 删除后的用户信息的消息提示
   */
  async deleteUser(id: number): Promise<string> {
    const user = await this.userRepository.findOne({ where: { id } });
    if (!user) {
      this.logger.warn(`删除失败：用户 ID ${id} 不存在`);
      return '用户不存在';
    }
    await this.userRepository.delete(id);
    this.logger.log(`用户 ${user.username} 删除成功`);
    return '删除成功';
  }

  /**
   * 修改密码
   * @param userId 用户ID
   * @param oldPassword 旧密码
   * @param newPassword 新密码
   * @returns 成功消息或错误消息
   */
  async changePassword(
    userId: number,
    oldPassword: string,
    newPassword: string,
  ): Promise<{ success: boolean; message: string }> {
    // 查找用户
    const user = await this.userRepository.findOne({ where: { id: userId } });

    if (!user) {
      this.logger.warn(`修改密码失败：用户 ID ${userId} 不存在`);
      return { success: false, message: '用户不存在' };
    }

    // 验证旧密码
    if (!user.password) {
      this.logger.warn(
        `修改密码失败：用户 ${user.username} 没有设置密码（可能是微信登录用户）`,
      );
      return { success: false, message: '该用户未设置密码，无法修改' };
    }
    const isOldPasswordValid = await bcrypt.compare(oldPassword, user.password);
    if (!isOldPasswordValid) {
      this.logger.warn(`修改密码失败：用户 ${user.username} 旧密码错误`);
      return { success: false, message: '旧密码错误' };
    }

    // 检查新密码是否与旧密码相同
    if (oldPassword === newPassword) {
      return { success: false, message: '新密码不能与旧密码相同' };
    }

    // 使用 bcrypt 对新密码进行哈希
    const saltRounds = 10;
    const hashedPassword = await bcrypt.hash(newPassword, saltRounds);

    // 更新密码
    user.password = hashedPassword;
    await this.userRepository.save(user);

    // 可选：修改密码后废除所有 Refresh Token（强制重新登录）
    await this.revokeAllUserTokens(userId);

    this.logger.log(`用户 ${user.username} 密码修改成功，所有设备已登出`);
    return { success: true, message: '密码修改成功，请重新登录' };
  }

  /**
   * 微信小程序登录
   * @param wechatLoginDto 微信登录信息
   * @param request Express Request 对象
   * @returns 用户信息和 Token
   */
  async wechatLogin(
    wechatLoginDto: WechatLoginDto,
    request?: Request,
  ): Promise<(AuthResponse & { refreshToken: string }) | null> {
    const { code, encryptedData, iv, nickname, avatarUrl, gender } =
      wechatLoginDto;

    // 1. 获取微信小程序配置
    const appId = this.configService.get<string>('wechat.appId');
    const appSecret = this.configService.get<string>('wechat.appSecret');

    if (!appId || !appSecret) {
      this.logger.error(
        '微信小程序配置未设置（WECHAT_APPID 或 WECHAT_APPSECRET）',
      );
      throw new UnauthorizedException('微信登录配置错误');
    }

    try {
      // 2. 使用 code 换取 openid 和 session_key
      const sessionData = await WechatUtil.code2Session(appId, appSecret, code);

      if (sessionData.errcode) {
        this.logger.error(
          `微信 code2Session 失败: ${sessionData.errcode} - ${sessionData.errmsg}`,
        );
        throw new UnauthorizedException(`微信登录失败: ${sessionData.errmsg}`);
      }

      const { openid, session_key, unionid } = sessionData;

      // 3. 解密手机号（如果提供了加密数据）
      let phone: string | undefined = undefined;
      if (encryptedData && iv && session_key) {
        try {
          const phoneData = WechatUtil.decryptPhoneNumber(
            encryptedData,
            iv,
            session_key,
          );
          phone = phoneData.purePhoneNumber || phoneData.phoneNumber;
          this.logger.log(`成功解密手机号: ${phone}`);
        } catch (error) {
          const errorMessage =
            error instanceof Error ? error.message : '未知错误';
          this.logger.warn(`解密手机号失败: ${errorMessage}`);
          // 手机号解密失败不影响登录流程，继续执行
        }
      }

      // 4. 查找用户（优先通过 openid，其次通过手机号）
      let user = await this.userRepository.findOne({
        where: { openid },
      });

      // 如果通过 openid 找不到，但有手机号，尝试通过手机号查找
      if (!user && phone) {
        user = await this.userRepository.findOne({
          where: { phone },
        });

        // 如果通过手机号找到了用户，绑定 openid
        if (user) {
          user.openid = openid;
          user.unionid = unionid;
          if (nickname) user.nickname = nickname;
          if (avatarUrl) user.avatar = avatarUrl;
          if (gender !== undefined) {
            user.gender =
              gender === 1 ? Gender.MALE : gender === 2 ? Gender.FEMALE : undefined;
          }
          await this.userRepository.save(user);
          this.logger.log(`为用户 ${user.id} 绑定了微信 openid: ${openid}`);
        }
      }

      // 5. 如果用户不存在，创建新用户
      if (!user) {
        // 生成默认用户名（使用昵称或 openid 的一部分）
        const defaultUsername = nickname
          ? `wx_${nickname}_${openid.slice(-6)}`
          : `wx_${openid.slice(-8)}`;

        // 检查用户名是否已存在
        let finalUsername = defaultUsername;
        let counter = 1;
        while (
          await this.userRepository.findOne({
            where: { username: finalUsername },
          })
        ) {
          finalUsername = `${defaultUsername}_${counter}`;
          counter++;
        }

        user = this.userRepository.create({
          username: finalUsername,
          phone: phone,
          openid,
          unionid: unionid,
          nickname: nickname,
          avatar: avatarUrl,
          gender:
            gender === 1 ? Gender.MALE : gender === 2 ? Gender.FEMALE : undefined,
          isActive: true,
        });

        await this.userRepository.save(user);
        this.logger.log(
          `微信用户注册成功: ${user.username} (openid: ${openid})`,
        );
      } else {
        // 用户已存在，更新最新的微信信息
        if (nickname) user.nickname = nickname;
        if (avatarUrl) user.avatar = avatarUrl;
        if (gender !== undefined) {
          user.gender =
            gender === 1 ? Gender.MALE : gender === 2 ? Gender.FEMALE : undefined;
        }
        if (phone && !user.phone) {
          user.phone = phone;
        }
        await this.userRepository.save(user);
        this.logger.log(
          `微信用户登录成功: ${user.username} (openid: ${openid})`,
        );
      }

      // 6. 生成 Access Token
      const payload: JwtPayload = {
        sub: user.id,
        username: user.username,
        email: user.email,
      };

      const accessToken = await this.jwtService.signAsync(payload);

      // 7. 生成 Refresh Token
      const refreshToken = await this.createRefreshToken(user.id, request);

      // 8. 查询好友总数
      const friendsCount = await this.friendshipRepository.count({
        where: [
          { userId: user.id, status: FriendshipStatus.ACCEPTED },
          { friendId: user.id, status: FriendshipStatus.ACCEPTED },
        ],
      });

      // 9. 返回用户信息和 token（排除密码字段）
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { password: _, ...safeUser } = user;

      return {
        user: {
          ...safeUser,
          friendsCount,
        },
        accessToken,
        refreshToken: refreshToken.token,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : '未知错误';
      const errorStack = error instanceof Error ? error.stack : undefined;
      this.logger.error(`微信登录失败: ${errorMessage}`, errorStack);
      throw new UnauthorizedException('微信登录失败，请稍后重试');
    }
  }

  /* ==================== Refresh Token 相关方法 ==================== */

  /**
   * 创建 Refresh Token
   * @param userId 用户 ID
   * @param request Express Request 对象
   * @param previousTokenId 上一个 Token 的 ID（用于 Token 轮换）
   * @param familyId Token 家族 ID（同一次登录产生的所有 Token 共享）
   * @returns RefreshToken 实体
   */
  async createRefreshToken(
    userId: number,
    request?: Request,
    previousTokenId?: number,
    familyId?: string,
  ): Promise<RefreshToken> {
    // 生成随机 Token（使用 UUID + 随机字符串增强安全性）
    const tokenValue = `${uuidv4()}.${crypto.randomBytes(32).toString('hex')}`;

    // 获取设备信息
    const deviceFingerprint = request
      ? DeviceFingerprintUtil.generate(request)
      : 'unknown';
    const ipAddress = request
      ? DeviceFingerprintUtil.getIpAddress(request)
      : 'unknown';
    const userAgent = request
      ? DeviceFingerprintUtil.getUserAgent(request)
      : 'unknown';

    // 计算过期时间（从配置读取，默认 7 天）
    const refreshTokenExpiresIn =
      this.configService.get<string>('jwt.refreshExpiresIn') || '7d';
    const expiresAt = this.calculateExpirationDate(refreshTokenExpiresIn);

    // 创建 Refresh Token 实体
    const refreshToken = this.refreshTokenRepository.create({
      token: tokenValue,
      userId,
      deviceFingerprint,
      ipAddress,
      userAgent,
      expiresAt,
      previousTokenId: previousTokenId,
      familyId: familyId || uuidv4(), // 新登录创建新的 family ID
    });

    await this.refreshTokenRepository.save(refreshToken);
    this.logger.log(`为用户 ${userId} 创建了新的 Refresh Token`);

    return refreshToken;
  }

  /**
   * 刷新 Access Token（实现 Token 轮换机制）
   * @param oldRefreshToken 旧的 Refresh Token
   * @param request Express Request 对象
   * @returns 新的 Access Token 和 Refresh Token
   */
  async refreshAccessToken(
    oldRefreshToken: string,
    request?: Request,
  ): Promise<TokenResponseDto> {
    // 查找 Refresh Token
    const tokenRecord = await this.refreshTokenRepository.findOne({
      where: { token: oldRefreshToken },
      relations: ['user'],
    });

    if (!tokenRecord) {
      this.logger.warn('刷新失败：Refresh Token 不存在');
      throw new UnauthorizedException('无效的 Refresh Token');
    }

    // 检查是否已过期
    if (new Date() > tokenRecord.expiresAt) {
      this.logger.warn(
        `刷新失败：Refresh Token 已过期（用户 ${tokenRecord.userId}）`,
      );
      await this.refreshTokenRepository.delete(tokenRecord.id);
      throw new UnauthorizedException('Refresh Token 已过期，请重新登录');
    }

    // 检查是否已废除
    if (tokenRecord.isRevoked) {
      this.logger.warn(
        `刷新失败：Refresh Token 已被废除（用户 ${tokenRecord.userId}）`,
      );
      throw new UnauthorizedException('Refresh Token 已被废除，请重新登录');
    }

    // 🚨 检测重复使用（可能被盗）
    if (tokenRecord.isUsed) {
      this.logger.error(
        `🚨 安全警报：检测到 Refresh Token 重复使用（用户 ${tokenRecord.userId}，Token Family ${tokenRecord.familyId}）`,
      );

      // 废除该 Token 家族的所有 Token
      await this.revokeTokenFamily(tokenRecord.familyId, tokenRecord.userId);

      throw new UnauthorizedException(
        '检测到异常行为，所有设备已登出，请重新登录',
      );
    }

    // 验证设备指纹（可选，严格模式）
    if (request && this.configService.get<boolean>('jwt.strictDeviceCheck')) {
      const currentFingerprint = DeviceFingerprintUtil.generate(request);
      if (currentFingerprint !== tokenRecord.deviceFingerprint) {
        this.logger.warn(
          `刷新失败：设备指纹不匹配（用户 ${tokenRecord.userId}）`,
        );
        await this.revokeToken(tokenRecord.id);
        throw new UnauthorizedException('设备验证失败，请重新登录');
      }
    }

    // 行为异常检测（可选）
    if (request) {
      await this.detectAnomalousActivity(tokenRecord, request);
    }

    // 标记旧 Token 为已使用
    tokenRecord.isUsed = true;
    tokenRecord.lastUsedAt = new Date();
    await this.refreshTokenRepository.save(tokenRecord);

    // 生成新的 Access Token
    const payload: JwtPayload = {
      sub: tokenRecord.user.id,
      username: tokenRecord.user.username,
      email: tokenRecord.user.email,
    };

    const accessToken = await this.jwtService.signAsync(payload);

    // 生成新的 Refresh Token（Token 轮换）
    const newRefreshToken = await this.createRefreshToken(
      tokenRecord.userId,
      request,
      tokenRecord.id, // 记录上一个 Token 的 ID
      tokenRecord.familyId, // 继承同一个 family ID
    );

    this.logger.log(`用户 ${tokenRecord.userId} 的 Token 刷新成功`);

    return {
      accessToken,
      refreshToken: newRefreshToken.token,
      tokenType: 'Bearer',
      expiresIn: this.getAccessTokenExpiresIn(),
    };
  }

  /**
   * 废除单个 Refresh Token
   * @param tokenId Token ID
   */
  async revokeToken(tokenId: number): Promise<void> {
    await this.refreshTokenRepository.update(tokenId, { isRevoked: true });
    this.logger.log(`Refresh Token ${tokenId} 已被废除`);
  }

  /**
   * 废除某个用户的所有 Refresh Token（用于登出所有设备）
   * @param userId 用户 ID
   */
  async revokeAllUserTokens(userId: number): Promise<void> {
    await this.refreshTokenRepository.update(
      { userId, isRevoked: false },
      { isRevoked: true },
    );
    this.logger.log(`用户 ${userId} 的所有 Refresh Token 已被废除`);
  }

  /**
   * 废除 Token 家族（检测到重复使用时）
   * @param familyId Token 家族 ID
   * @param userId 用户 ID
   */
  async revokeTokenFamily(familyId: string, userId: number): Promise<void> {
    await this.refreshTokenRepository.update(
      { familyId, isRevoked: false },
      { isRevoked: true },
    );
    this.logger.error(
      `🚨 用户 ${userId} 的 Token 家族 ${familyId} 已被全部废除（安全事件）`,
    );
  }

  /**
   * 登出（废除指定的 Refresh Token）
   * @param refreshToken Refresh Token
   */
  async logout(refreshToken: string): Promise<void> {
    const tokenRecord = await this.refreshTokenRepository.findOne({
      where: { token: refreshToken },
    });

    if (tokenRecord) {
      await this.revokeToken(tokenRecord.id);
      this.logger.log(`用户 ${tokenRecord.userId} 登出成功`);
    }
  }

  /**
   * 登出所有设备
   * @param userId 用户 ID
   */
  async logoutAllDevices(userId: number): Promise<void> {
    await this.revokeAllUserTokens(userId);
    this.logger.log(`用户 ${userId} 已从所有设备登出`);
  }

  /**
   * 清理过期的 Refresh Token（定时任务调用）
   */
  async cleanupExpiredTokens(): Promise<void> {
    const result = await this.refreshTokenRepository.delete({
      expiresAt: LessThan(new Date()),
    });

    this.logger.log(`清理了 ${result.affected || 0} 个过期的 Refresh Token`);
  }

  /**
   * 获取用户的活跃 Token 列表（用于管理多设备登录）
   * @param userId 用户 ID
   * @returns RefreshToken 列表
   */
  async getUserActiveTokens(userId: number): Promise<RefreshToken[]> {
    return this.refreshTokenRepository.find({
      where: {
        userId,
        isRevoked: false,
        isUsed: false,
        expiresAt: MoreThan(new Date()),
      },
      order: { createdAt: 'DESC' },
    });
  }

  /**
   * 异常行为检测
   * @param tokenRecord Refresh Token 记录
   * @param request Express Request 对象
   */
  private async detectAnomalousActivity(
    tokenRecord: RefreshToken,
    request: Request,
  ): Promise<void> {
    const currentIp = DeviceFingerprintUtil.getIpAddress(request);
    const currentDevice = DeviceFingerprintUtil.generate(request);

    // 获取最近1小时的刷新次数
    const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
    const recentRefreshCount = await this.refreshTokenRepository.count({
      where: {
        userId: tokenRecord.userId,
        createdAt: MoreThan(oneHourAgo),
      },
    });

    // 计算风险评分
    const riskScore = DeviceFingerprintUtil.calculateRiskScore({
      currentIp,
      lastKnownIp: tokenRecord.ipAddress,
      currentDevice,
      lastKnownDevice: tokenRecord.deviceFingerprint,
      refreshCount: recentRefreshCount,
    });

    // 风险阈值（可配置）
    const riskThreshold =
      this.configService.get<number>('jwt.riskThreshold') || 60;

    if (riskScore >= riskThreshold) {
      this.logger.warn(
        `⚠️  用户 ${tokenRecord.userId} 的刷新行为风险评分较高：${riskScore}（IP: ${currentIp}）`,
      );

      // 这里可以触发额外的安全措施：
      // 1. 发送安全通知
      // 2. 要求二次验证
      // 3. 记录安全日志
      // 暂时只记录日志，不阻止刷新
    }
  }

  /**
   * 计算过期时间
   * @param expiresIn 过期时间字符串（如 '7d', '24h'）
   * @returns Date 对象
   */
  private calculateExpirationDate(expiresIn: string): Date {
    const now = new Date();
    const match = expiresIn.match(/^(\d+)([dhms])$/);

    if (!match) {
      // 默认 7 天
      return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
    }

    const value = parseInt(match[1], 10);
    const unit = match[2];

    switch (unit) {
      case 'd':
        return new Date(now.getTime() + value * 24 * 60 * 60 * 1000);
      case 'h':
        return new Date(now.getTime() + value * 60 * 60 * 1000);
      case 'm':
        return new Date(now.getTime() + value * 60 * 1000);
      case 's':
        return new Date(now.getTime() + value * 1000);
      default:
        return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
    }
  }

  /**
   * 获取 Access Token 的过期时间（秒）
   * @returns 过期时间（秒）
   */
  private getAccessTokenExpiresIn(): number {
    const expiresIn = this.configService.get<string>('jwt.expiresIn') || '1h';
    const match = expiresIn.match(/^(\d+)([dhms])$/);

    if (!match) return 3600; // 默认 1 小时

    const value = parseInt(match[1], 10);
    const unit = match[2];

    switch (unit) {
      case 'd':
        return value * 24 * 60 * 60;
      case 'h':
        return value * 60 * 60;
      case 'm':
        return value * 60;
      case 's':
        return value;
      default:
        return 3600;
    }
  }
}
